<?php

class WoWCharacterParser {

  var $character = array();
  var $depth = array();
  var $data;
  var $updated;
  
  // returns 0 on success, 1 on failure, holds data in $this->data.
  function loadArenaData($z, $b) {
  	$this->data = "";
  	$this->character = array();
  	$this->depth = array();
  	$cache = 'cache/'.$z.'/'.$b.'_5s_top20.xml';
  	$url = ($z == 'eu') ? 'http://armory.wow-europe.com/arena-ladder.xml?b='.$b.'&ts=5&p=1&sf=rank&sd=a' : (($z == 'kr') ? 'http://armory.worldofwarcraft.co.kr/arena-ladder.xml?b='.$b.'&ts=5&p=1&sf=rank&sd=a' : 'http://armory.worldofwarcraft.com/arena-ladder.xml?b='.$b.'&ts=5&p=1&sf=rank&sd=a');
  	$referrer = ($z == 'eu') ? 'http://armory.wow-europe.com/' : (($z == 'kr') ? 'http://armory.worldofwarcraft.co.kr/' : 'http://armory.worldofwarcraft.com/');
  	if (file_exists($cache)) {
  		if (filemtime($cache) < (time()-(6*60*60))) { // older than 6hrs?
  			if ($this->retrieveFromURL($referrer, $url) == 0) { // ok, retrieving
  				$this->updated = true;
  				return 0;
  			} else {
  				// update filetime! (so it only looks every 30min)
  				touch($cache);
  			}
  		}
  		// retrieve from cache, either up to date, or failed to retrieve new
  		$this->updated = false;
  		return $this->retrieveFromFile($cache);
  	} else { // no cache, attempt to get
  		$this->updated = true;
  		return $this->retrieveFromURL($referrer, $url);
  	}
  }
  
  /* returns 0 on success, 1 on failure. data goes into $this->data */
  function retrieveFromFile($cache) {
  	// grab from file
		$fp = fopen($cache, 'r');
		if (!$fp) {
			return 1;
		}
		$this->data = fread($fp, filesize($cache));
		fclose($fp);
		return 0;
  }
  
  function retrieveFromURL($referrer, $url) {
  	// grab from armory
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_REFERER, $referrer);
		// cos they do browser detection! :O
		curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2");
		curl_setopt($ch, CURLOPT_HEADER, 0);
		curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
		$this->data = curl_exec($ch);
		curl_close($ch);
		
		return 0;
  }
  
  /* writes the data in the class to the cache file */
  /* the last parameter is if we should overwrite existing data or not */
  function writeCache($z, $r, $n, $overwrite) {
  	$cache = 'cache/'.$z.'/'.$r.'/'.$n.'.xml';
  	$cachedir = 'cache/'.$z.'/'.$r;
  	if (!$this->updated) return; // if not new data, don't write to file.
  	// check if file exists
  	if (!$overwrite) {
  		if (file_exists($cache)) {
  			return 1;
  		}
  	}
  	// make sure realm dir exists
  	if (!file_exists($cachedir)) {
  		mkdir($cachedir, 0755); // read only for others
  	}
  	$fp = fopen($cache, 'w');
		if (!$fp) {
			return 1;
		}
		fwrite($fp, $this->data);
		fclose($fp);
		return 0;
  }
  
  function parseXML() {
  	/* parse the data */
		$xml_parser = xml_parser_create('UTF-8');
		
		xml_set_object($xml_parser, $this);
		xml_set_element_handler($xml_parser, "startElement", "endElement");
		xml_set_character_data_handler($xml_parser, "characterData");
		
		xml_parse($xml_parser, $this->data);
		
		xml_parser_free($xml_parser);
  }
  
  /* recursive function to add data back in */
  function dataAdd(&$char, $depth, $attrs) {
  	if (count($depth) == 1) { // reached the end
  		$count = count($char[$depth[0]]);
  		if ($count == 0) {
  			$char[$depth[0]] = array();
  		}
  		$char[$depth[0]][] = array('attr' => $attrs,
  														'data' => '',
  														'children' => array());
  	} else {
  		// go deeper
  		$key = array_shift($depth);
  		$count = count($char[$key]);
  		$this->dataAdd($char[$key][$count-1]['children'], $depth, $attrs);
  	}
  }
  
	/* push onto depth array, and add to char array */
	function startElement($parser, $tagName, $attrs) {
 		$newattr = array();
		foreach($attrs as $key=>$value) {
			$newattr[mb_strtolower($key)] = $value;
		}
		array_push($this->depth, mb_strtolower($tagName));
		$this->dataAdd($this->character, $this->depth, $newattr);
	}

   function endElement($parser, $tagName) {
   		array_pop($this->depth);
   }

   function characterData($parser, $data) {
		/* there is no useful char data in the blizzard armory xml */
   }

   /* Various accessor functions for parts of the data follow
   	 	These are just here to make code using this class 'tidier'.
   */
   
  /* gets an attribute value, sorta xpath-like */
  // eg input: 'page/characterinfo/character/name'
  // eg input: 'page/arenaladderpagedresult/arenateams/arenateam[0]/battlegroup'
	function getAttrib($path) {
		$apath = explode('/', mb_strtolower($path));
		$attrib = array_pop($apath);
		$curr = $this->character[array_shift($apath)]; // root, cannot have siblings
		$now = 0;
		$next = 0;
		foreach ($apath as $xpath) {
			// check for [xxx] syntax, if found, store in next and continue;
			$matches = array();
			if (preg_match('/^(.*)\[([0-9]+)\]$/', $xpath, $matches) > 0) {
				// match
				$xpath = $matches[1];
				$next = $matches[2];
			}
			$curr = $curr[$now]['children'][$xpath];
			$now = $next;
			$next = 0;
		}
		return $curr[$now]['attr'][$attrib];
	}
  
	function getCharacter($key) {
		return $this->character['page'][0]['children']['characterinfo'][0]['children']['character'][0]['attr'][mb_strtolower($key)];
	}

	function getArena() {
		return $this->character['page'][0]['children']['characterinfo'][0]['children']['character'][0]['children']['arenateams'][0]['children']['arenateam'];
	}

	function getCharacterTab($key) {
		return $this->character['page'][0]['children']['characterinfo'][0]['children']['charactertab'][0]['children'][mb_strtolower($key)];
	}

	function getSkillTab($key) {
	}

	function getReputationTab() {
		return $this->character['page'][0]['children']['characterinfo'][0]['children']['reputationtab'][0]['children']['factioncategory'];
	}
}

?>